package top.lixunda.ecommerce.server.good.api.entity.dto;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.google.common.collect.ImmutableMap;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import top.lixunda.common.utils.ObjectValidators;
import top.lixunda.ecommerce.server.business.api.condition.ILambdaQueryWrapperDTO;
import top.lixunda.ecommerce.server.business.api.entity.dto.OrderByDTO;
import top.lixunda.ecommerce.server.good.api.entity.db.GoodsDO;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

/**
 * 条件查询商品数据信息，包括分页
 *
 * @author Xumda
 * @version time:2019/12/17 14:01
 */
@SuppressWarnings("Duplicates")
@ApiModel("条件查询商品数据信息DTO")
@Slf4j
@Data
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = false)
public class GoodsQueryDTO extends OrderByDTO
        implements ILambdaQueryWrapperDTO<GoodsDO> {

    /**
     * 根据商品名称查询
     */
    @ApiModelProperty(hidden = true)
    @JsonIgnore
    public static final String SEARCH_TYPE_GOODS_NAME = "name";

    /**
     * 根据商品类别查询
     */
    @ApiModelProperty(hidden = true)
    @JsonIgnore
    public static final String SEARCH_TYPE_TYPE = "type";

    /**
     * 默认查询
     */
    @ApiModelProperty(hidden = true)
    @JsonIgnore
    public static final String SEARCH_TYPE_DEFAULT = "default";

    /**
     * 搜索类型，如普通所有，店铺搜索，类别搜索等
     */
    private String searchType;

    /**
     * 搜索字符串
     */
    private List<String> search;
    /**
     * 商品id
     */
    private List<Integer> goodId;

    private List<Integer> groupId;

    private List<String> groupName;

    private List<Integer> storeId;

    private List<String> storeName;

    private List<Integer> goodCategoryId;

    private List<String> goodName;

    private List<Integer> createGoodUserId;

    private List<Integer> checkGoodUserId;

    private List<String> goodState;

    private LocalDateTime createGoodTimeBegin;

    private LocalDateTime createGoodTimeEnd;

    private LocalDateTime checkGoodTimeBegin;

    private LocalDateTime checkGoodTimeEnd;

    private LocalDateTime pushGoodTimeBegin;

    private LocalDateTime pushGoodTimeEnd;

    private BigDecimal goodScoreBegin;

    private BigDecimal goodScoreEnd;

    /**
     * 处理搜索条件
     *
     * @param wrapper 条件构造器
     */
    private void dealWithSearch(LambdaQueryWrapper<GoodsDO> wrapper) {
        if (ObjectValidators.isNotEmpty(this.search) && ObjectValidators.isNotEmpty(this.searchType)) {
            switch (searchType) {
                // default
                case GoodDetailQueryDTO.SEARCH_TYPE_DEFAULT:
                    wrapper.and(innerWrapper ->
                            this.getSearch().stream()
                                    .filter(ObjectValidators::isNotEmpty)
                                    .forEach(item -> innerWrapper.or().like(GoodsDO::getName, item)
                                            .or().like(GoodsDO::getGoodCategoryName, item)));
                    break;
                // name
                case GoodDetailQueryDTO.SEARCH_TYPE_GOODS_NAME:
                    wrapper.and(innerWrapper ->
                            this.getSearch().stream()
                                    .filter(ObjectValidators::isNotEmpty)
                                    .forEach(item -> innerWrapper.or().like(GoodsDO::getName, item)));
                    break;
                // type
                case GoodDetailQueryDTO.SEARCH_TYPE_TYPE:
                    wrapper.and(innerWrapper ->
                            this.getSearch().stream()
                                    .filter(ObjectValidators::isNotEmpty)
                                    .forEach(item -> innerWrapper.or().like(GoodsDO::getGoodCategoryName, item)));
                    break;
                default:
                    log.debug("不存在的搜索方式 {}", this.searchType);
            }
        }
    }

    /**
     * 处理时间相关
     *
     * @param wrapper 条件构造器
     */
    private void dealWithTime(LambdaQueryWrapper<GoodsDO> wrapper) {
        if (ObjectValidators.isNotEmpty(this.getCreateGoodTimeBegin())) {
            wrapper.ge(GoodsDO::getCreateTime, this.getCreateGoodTimeBegin());
        }
        if (ObjectValidators.isNotEmpty(this.getCreateGoodTimeEnd())) {
            wrapper.le(GoodsDO::getCreateTime, this.getCreateGoodTimeEnd());
        }
        if (ObjectValidators.isNotEmpty(this.getCheckGoodTimeBegin())) {
            wrapper.ge(GoodsDO::getCheckTime, this.getCheckGoodTimeBegin());
        }
        if (ObjectValidators.isNotEmpty(this.getCheckGoodTimeEnd())) {
            wrapper.le(GoodsDO::getCheckTime, this.getCheckGoodTimeEnd());
        }
        if (ObjectValidators.isNotEmpty(this.getPushGoodTimeBegin())) {
            wrapper.ge(GoodsDO::getPushTime, this.getPushGoodTimeBegin());
        }
        if (ObjectValidators.isNotEmpty(this.getPushGoodTimeEnd())) {
            wrapper.le(GoodsDO::getPushTime, this.getPushGoodTimeEnd());
        }
    }

    @ApiModelProperty(hidden = true)
    @JsonIgnore
    private static final Map<String, SFunction<GoodsDO, ?>> MAP =
            new ImmutableMap.Builder<String, SFunction<GoodsDO, ?>>()
                    .put("goodId", GoodsDO::getId)
                    .put("groupId", GoodsDO::getGroupId)
                    .put("groupName", GoodsDO::getGroupName)
                    .put("storeId", GoodsDO::getStoreId)
                    .put("storeName", GoodsDO::getStoreName)
                    .put("createUserId", GoodsDO::getCreateUserId)
                    .put("createUserName", GoodsDO::getCreateUserName)
                    .put("updateUserId", GoodsDO::getUpdateUserId)
                    .put("updateUserName", GoodsDO::getUpdateUserName)
                    .put("removeUserId", GoodsDO::getRemoveUserId)
                    .put("removeUserName", GoodsDO::getRemoveUserName)
                    .put("createTime", GoodsDO::getCreateTime)
                    .put("updateTime", GoodsDO::getUpdateTime)
                    .put("removeTime", GoodsDO::getRemoveTime)
                    .put("name", GoodsDO::getName)
                    .put("simpleDesc", GoodsDO::getSimpleDesc)
                    .put("description", GoodsDO::getDescription)
                    .put("goodCategoryId", GoodsDO::getGoodCategoryId)
                    .put("goodCategoryName", GoodsDO::getGoodCategoryName)
                    .put("sellPoint", GoodsDO::getSellPoint)
                    .put("score", GoodsDO::getScore)
                    .put("scoreUpdateTime", GoodsDO::getScoreUpdateTime)
                    .put("freight", GoodsDO::getFreight)
                    .put("pushTime", GoodsDO::getPushTime)
                    .put("pullTime", GoodsDO::getPullTime)
                    .put("goodState", GoodsDO::getState)
                    .put("goodStateDesc", GoodsDO::getStateDesc)
                    .put("checkUserId", GoodsDO::getCheckUserId)
                    .put("checkUserName", GoodsDO::getCheckUserName)
                    .put("checkTime", GoodsDO::getCheckTime)
                    .build();

    /**
     * 将当前用于查询的DTO中的查询数据转换为 LambdaQuerySqlWrapper
     *
     * @return 条件构造器 LambdaQuerySqlWrapper
     */
    @Override
    public LambdaQueryWrapper<GoodsDO> toWrapper() {
        LambdaQueryWrapper<GoodsDO> wrapper = new LambdaQueryWrapper<>();
        if (ObjectValidators.isNotEmpty(this.getGoodId())) {
            wrapper.and(innerWrapper -> this.getGoodId().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getId, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getGoodName())) {
            wrapper.and(innerWrapper -> this.getGoodName().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getName, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getGroupId())) {
            wrapper.and(innerWrapper -> this.getGroupId().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getGroupId, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getGroupName())) {
            wrapper.and(innerWrapper -> this.getGroupName().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getGroupName, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getStoreId())) {
            wrapper.and(innerWrapper -> this.getStoreId().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getStoreId, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getStoreName())) {
            wrapper.and(innerWrapper -> this.getStoreName().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getStoreName, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getGoodCategoryId())) {
            wrapper.and(innerWrapper -> this.getGoodCategoryId().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getGoodCategoryId, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getCreateGoodUserId())) {
            wrapper.and(innerWrapper -> this.getCreateGoodUserId().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getCreateUserId, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getCheckGoodUserId())) {
            wrapper.and(innerWrapper -> this.getCheckGoodUserId().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getCheckUserId, item)));
        }
        if (ObjectValidators.isNotEmpty(this.getGoodState())) {
            wrapper.and(innerWrapper -> this.getGoodState().forEach(item ->
                    innerWrapper.or().eq(GoodsDO::getState, item)));
        }

        if (ObjectValidators.isNotEmpty(this.getGoodScoreBegin())) {
            wrapper.ge(GoodsDO::getScore, this.getGoodScoreBegin());
        }
        if (ObjectValidators.isNotEmpty(this.getGoodScoreEnd())) {
            wrapper.le(GoodsDO::getScore, this.getGoodScoreEnd());
        }

        dealWithSearch(wrapper);
        dealWithTime(wrapper);
        dealWithOrderBy(wrapper, MAP);
        return wrapper;
    }
}
